package com.ssy.lingxi.workflow.serviceimpl;

import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.member.merchant.api.feign.inner.MemberInnerControllerFeign;
import com.ssy.lingxi.member.merchant.api.model.vo.inner.response.MemberFeignRoleVO;
import com.ssy.lingxi.workflow.api.model.vo.request.AuthUrlVO;
import com.ssy.lingxi.workflow.api.model.vo.request.ProcessMemberRoleVO;
import com.ssy.lingxi.workflow.api.model.vo.request.ProcessRemoveMemberRoleVO;
import com.ssy.lingxi.workflow.entity.ExternalProcessRole;
import com.ssy.lingxi.workflow.entity.InternalProcessRole;
import com.ssy.lingxi.workflow.model.bo.MemberRoleBO;
import com.ssy.lingxi.workflow.model.constant.ModelConstant;
import com.ssy.lingxi.workflow.model.constant.WorkflowStringEnum;
import com.ssy.lingxi.workflow.repository.ExternalProcessRoleRepository;
import com.ssy.lingxi.workflow.repository.InternalProcessRoleRepository;
import com.ssy.lingxi.workflow.service.IInternationalizeService;
import com.ssy.lingxi.workflow.service.IProcessRoleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 流程关联的会员角色、用户角色查询接口实现类
 *
 * @author 万宁
 * @version 2.0.0
 * @date 2021-04-28
 */
@Service
public class ProcessRoleServiceImpl implements IProcessRoleService {
    private static final Logger logger = LoggerFactory.getLogger(ProcessRoleServiceImpl.class);

    @Resource
    private IInternationalizeService internationalizeService;

    @Resource
    private ExternalProcessRoleRepository externalProcessRoleRepository;

    @Resource
    private InternalProcessRoleRepository internalProcessRoleRepository;

    @Resource
    private MemberInnerControllerFeign memberInnerControllerFeign;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 新增/修改用户角色关联的菜单Url设置
     *
     * @param processMemberRoleVO 接口参数
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void updateInsertMemberRoleAuth(ProcessMemberRoleVO processMemberRoleVO) {
        List<InternalProcessRole> internalProcessRoles = internalProcessRoleRepository.findByUrlIn(processMemberRoleVO.getUrls().stream().map(AuthUrlVO::getUrl).collect(Collectors.toList()));

        //根据Url + Source进行筛选，匹配 memberId + roleId，修改角色名称
        List<InternalProcessRole> updateList = new ArrayList<>();
        processMemberRoleVO.getUrls().forEach(authUrlVO -> internalProcessRoles.stream().filter(config -> config.getUrl().equals(authUrlVO.getUrl()) && config.getSource().equals(authUrlVO.getSource())).findFirst().ifPresent(internalProcessRole -> {
            MemberRoleBO roleBO = internalProcessRole.getRoles().stream().filter(memberRoleBO -> memberRoleBO.getMemberId().equals(processMemberRoleVO.getMemberId()) && memberRoleBO.getMemberRoleId().equals(processMemberRoleVO.getMemberRoleId())).findFirst().orElse(null);
            if (roleBO == null) {
                internalProcessRole.getRoles().add(new MemberRoleBO(processMemberRoleVO.getMemberId(), processMemberRoleVO.getMemberRoleId(), processMemberRoleVO.getMemberRoleName()));
            } else {
                roleBO.setMemberRoleName(processMemberRoleVO.getMemberRoleName());
            }
            updateList.add(internalProcessRole);
        }));

        updateList.addAll(processMemberRoleVO.getUrls().stream().filter(authUrlVO -> internalProcessRoles.stream().noneMatch(config -> config.getUrl().equals(authUrlVO.getUrl()) && config.getSource().equals(authUrlVO.getSource()))).map(authUrlVO -> {
            InternalProcessRole authConfig = new InternalProcessRole();
            authConfig.setUrl(authUrlVO.getUrl());
            authConfig.setSource(authUrlVO.getSource());
            authConfig.setRoles(Stream.of(new MemberRoleBO(processMemberRoleVO.getMemberId(), processMemberRoleVO.getMemberRoleId(), processMemberRoleVO.getMemberRoleName())).collect(Collectors.toList()));
            return authConfig;
        }).collect(Collectors.toList()));

        internalProcessRoleRepository.saveAll(updateList);
    }

    /**
     * 从现有的菜单Url配置中，移除会员自定义角色
     *
     * @param processRemoveMemberRoleVO 接口参数
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void removeMemberRole(ProcessRemoveMemberRoleVO processRemoveMemberRoleVO) {
        List<InternalProcessRole> internalProcessRoles = internalProcessRoleRepository.findAll();
        internalProcessRoles.forEach(config -> config.getRoles().removeIf(memberRoleBO -> memberRoleBO.getMemberId().equals(processRemoveMemberRoleVO.getMemberId()) && memberRoleBO.getMemberRoleId().equals(processRemoveMemberRoleVO.getMemberRoleId())));
        internalProcessRoleRepository.saveAll(internalProcessRoles);
    }

    /**
     * 查询外部流程的会员角色名称
     *
     * @param roleId 角色Id
     * @return 角色名称
     */
    @Override
    public String findExternalTaskRoleName(Long roleId) {
        try {
            String roleName = stringRedisTemplate.opsForValue().get(ModelConstant.ROLE_CACHE_KEY.concat(String.valueOf(roleId)));
            if(StringUtils.hasLength(roleName)) {
                return roleName;
            }

            ExternalProcessRole externalProcessRole = externalProcessRoleRepository.findFirstByRoleId(roleId);
            if (externalProcessRole != null) {
                stringRedisTemplate.opsForValue().set(ModelConstant.ROLE_CACHE_KEY.concat(String.valueOf(roleId)), externalProcessRole.getRoleName());
                return externalProcessRole.getRoleName();
            }

            Wrapper<List<MemberFeignRoleVO>> result = memberInnerControllerFeign.getRoles(Stream.of(roleId).collect(Collectors.toList()));

            if (result.getCode() == ResponseCode.SUCCESS.getCode() && !CollectionUtils.isEmpty(result.getData())) {
                MemberFeignRoleVO roleVO = result.getData().stream().filter(memberFeignRoleVO -> memberFeignRoleVO.getRoleId().equals(roleId)).findFirst().orElse(null);
                if (roleVO != null) {
                    externalProcessRole = new ExternalProcessRole();
                    externalProcessRole.setRoleId(roleVO.getRoleId());
                    externalProcessRole.setRoleName(roleVO.getRoleName());
                    externalProcessRoleRepository.saveAndFlush(externalProcessRole);

                    stringRedisTemplate.opsForValue().set(ModelConstant.ROLE_CACHE_KEY.concat(String.valueOf(roleId)), externalProcessRole.getRoleName());

                    return roleVO.getRoleName();
                }
            } else {
                logger.error("从会员服务查询角色名称错误：" + result.getMessage());
            }

            return "";
        } catch (Exception e) {
            logger.error("从会员服务查询角色名称错误", e);
            return "";
        }
    }

    /**
     * 查询内部流程的用户角色名称
     *
     * @param memberId 会员Id
     * @param urls     流程关联的url列表
     * @param source   菜单所在客户端枚举
     * @return 用户角色名称
     */
    @Override
    public Map<String, String> findInternalTaskRoleName(Long memberId, List<String> urls, Integer source) {
        if (CollectionUtils.isEmpty(urls)) {
            return new HashMap<>();
        }

        List<InternalProcessRole> internalProcessRoles = internalProcessRoleRepository.findBySourceAndUrlIn(source, urls);
        if (CollectionUtils.isEmpty(internalProcessRoles)) {
            return new HashMap<>();
        }

        Map<String, String> map = new HashMap<>();
        internalProcessRoles.forEach(internalProcessRole -> {
            String url = internalProcessRole.getUrl();
            List<MemberRoleBO> memberRoles = internalProcessRole.getRoles().stream().filter(memberRoleBO -> memberRoleBO.getMemberId().equals(memberId)).collect(Collectors.toList());
            String roleNames = memberRoles.stream().map(MemberRoleBO::getMemberRoleName).collect(Collectors.joining(","));
            map.putIfAbsent(url, StringUtils.hasLength(roleNames) ? roleNames : internationalizeService.findMessage(WorkflowStringEnum.DEFAULT_INTERNAL_PROCESS_ROLE_NAME));
        });

        return map;
    }
}
